home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 41 / Amiga Format CD41 (1999-06)(Future Publishing)(GB)[!][issue 1999-07].iso / -seriously_amiga- / graphics / mountainview / gui.c next >
C/C++ Source or Header  |  1999-04-28  |  25KB  |  740 lines

  1. /*========================================================================*\
  2.  |  File: gui.c                                        Date: 23 Jan 1999  |
  3.  *------------------------------------------------------------------------*
  4.  |      Creates windows and requesters, all this stuff that requires      |
  5.  |  filling in huge structs and arrays. Doesn't do any input processing,  |
  6.  |               except for some requester-like functions.                |
  7.  |                                                                        |
  8. \*========================================================================*/
  9.  
  10.  
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <assert.h>
  15. #include <proto/gadtools.h>
  16. #include <proto/intuition.h>
  17. #include <proto/graphics.h>
  18. #include <proto/asl.h>
  19. #include <proto/exec.h>
  20. #include <proto/dos.h>
  21. #include <proto/wb.h>
  22. #include <proto/icon.h>
  23. #include <graphics/gfxbase.h>
  24. #include <intuition/gadgetclass.h>
  25.  
  26. #include "gui.h"
  27.  
  28. /* Simplified version information that controls some of our features.  */
  29. BOOL fGTV39  = FALSE;
  30. BOOL fIntV39 = FALSE;
  31. BOOL fGfxV39 = FALSE;
  32.  
  33. /* Define and nullify the libbases, so libnix doesn't auto-open them. */
  34. struct GfxBase       *GfxBase       = NULL;
  35. struct IntuitionBase *IntuitionBase = NULL;
  36. struct Library       *GadToolsBase  = NULL;
  37. struct Library       *AslBase       = NULL;
  38.  
  39.  
  40. enum {
  41.     MSRT_STRING = 0,
  42.     MSRT_LONG,
  43.     MSRT_FLOAT
  44.     };
  45.  
  46. typedef struct          /* Describe one line in a MultiStringRequest(). */
  47.     {
  48.     STRPTR strLabel;    /* label in front of this string gadget */
  49.     WORD wType;         /* one of the above MSRT_XXX-constants */
  50.     union {
  51.         STRPTR strBuf;
  52.         LONG     lVal;
  53.         FLOAT   flVal;
  54.         } val;
  55.     WORD wBufSize;              /* only needed with MSRT_STRING */
  56.     struct Gadget *pgad;        /* for internal purposes */
  57.     } MSR_ITEM;
  58.  
  59.  
  60. struct Window   *pwinMain = NULL;
  61. struct Menu     *menu = NULL;
  62. struct Screen   *scr = NULL;
  63. APTR            visualInfo = NULL;
  64. struct TextFont *defFont = NULL;
  65. struct TextAttr defFontA;
  66. LONG   laPens[ 32 ];
  67.  
  68. /* Propgadgets, must be plain intuition (no gadtools) gadgets. */
  69. struct Image imgRise, imgSpin;
  70. struct PropInfo piRise =
  71.     {
  72.     AUTOKNOB | PROPNEWLOOK | FREEVERT,
  73.     MAXPOT, MAXPOT / 2,         /* start at 45° */
  74.     MAXBODY, MAXBODY / 10       /* 0 - 90° at 10° granularity */
  75.     };
  76. struct PropInfo piSpin =
  77.     {
  78.     AUTOKNOB | PROPNEWLOOK | FREEHORIZ,
  79.     MAXPOT / 12 * 5, MAXPOT,    /* start at 150° */
  80.     MAXBODY / 13, MAXBODY       /* 0 - 360° at 30° granularity */
  81.     };
  82. struct Gadget gadRise =
  83.     {
  84.     NULL,           /* no link */
  85.     0, 0, 0, 0,     /* we'll fill in the dimensions later */
  86.     GFLG_RELHEIGHT | GFLG_RELRIGHT,
  87.     GACT_RELVERIFY | GACT_IMMEDIATE | GACT_RIGHTBORDER,
  88.     GTYP_PROPGADGET,
  89.     &imgRise, NULL, NULL, 0,
  90.     &piRise, IDC_HEIGHT, NULL
  91.     };
  92. struct Gadget gadSpin =
  93.     {
  94.     NULL,
  95.     0, 0, 0, 0,
  96.     GFLG_RELWIDTH | GFLG_RELBOTTOM,
  97.     GACT_RELVERIFY | GACT_IMMEDIATE | GACT_BOTTOMBORDER,
  98.     GTYP_PROPGADGET,
  99.     &imgSpin, NULL, NULL, 0,
  100.     &piSpin, IDC_ROTATION, NULL
  101.     };
  102. /* Oh, and one button gadget. */
  103. struct Gadget gadCmd =
  104.     {
  105.     NULL,
  106.     0, 0, 0, 0,
  107.     GFLG_GADGHCOMP | GFLG_RELRIGHT,
  108.     GACT_RELVERIFY | GACT_RIGHTBORDER,
  109.     GTYP_BOOLGADGET,
  110.     /*border*/NULL, NULL, NULL, 0,
  111.     NULL, IDC_STOP_GO, NULL
  112.     };
  113.  
  114.  
  115. #define AREA_BYTES 100          /* small buffer for simple area fills */
  116. WORD waArBuf[ AREA_BYTES/2 ];
  117. struct AreaInfo ari;
  118. struct TmpRas  tras;
  119. void  *ras;
  120. WORD  wMaxX, wMaxY;             /* we'll allocate a raster of this size */
  121.  
  122. #define FNAME_SIZE 40           /* only for the file part of a path name */
  123.  
  124. /* Shortcut, so the flags don't clutter the layout of the menu table. */
  125. #define CM  CHECKIT | MENUTOGGLE
  126.  
  127. struct NewMenu nmaMenu[] =
  128. {
  129.     { NM_TITLE, "Mountain",     NULL, 0,   0, NULL },
  130.     {  NM_ITEM, "New map",       "N", 0,   0, (APTR)IDM_NEW },
  131.     {  NM_ITEM, "Border",        "B", CM,  0, (APTR)IDM_BORDER },
  132.     {  NM_ITEM, "Auto redraw",   "A", CM,  0, (APTR)IDM_AUTODRAW },
  133.     {  NM_ITEM, "Draw",          "D", 0,   0, (APTR)IDM_DRAW },
  134.     {  NM_ITEM, "Stop drawing",  "X", 0,   0, (APTR)IDM_ABORT },
  135.     {  NM_ITEM, NM_BARLABEL,    NULL, 0,   0, NULL },
  136.     {  NM_ITEM, "Quit",          "Q", 0,   0, (APTR)IDM_QUIT },
  137.  
  138.     { NM_TITLE, "Window",       NULL, 0,   0, NULL },
  139.     {  NM_ITEM, "Maximize",      "M", 0,   0, (APTR)IDM_MAX },
  140.     {  NM_ITEM, "Center",        "C", 0,   0, (APTR)IDM_CENTER },
  141.     {  NM_ITEM, "Zoom",          "Z", 0,   0, (APTR)IDM_ZOOM },
  142.  
  143.     {   NM_END, NULL,           NULL, 0,   0, NULL }
  144. };
  145.  
  146. #undef CM
  147.  
  148.  
  149. /*------------------------------------------------------------------------*\
  150.         This is called upon exit and closes libraries and such stuff.
  151.      Zeroes all its pointers, so it can be safely called more than once.
  152. \*------------------------------------------------------------------------*/
  153.  
  154. VOID LibClose( struct Library **ppLib )
  155.     {
  156.     if( *ppLib )
  157.         {
  158.         CloseLibrary( *ppLib );
  159.         *ppLib = NULL;
  160.         }
  161.     }
  162.  
  163. VOID GetMeOutOfHere()
  164.     {
  165.     LibClose( (struct Library **)&IntuitionBase );
  166.     LibClose( (struct Library **)&GfxBase );
  167.     LibClose( &GadToolsBase );
  168.     LibClose( &AslBase );
  169.     }
  170.  
  171.  
  172. /*------------------------------------------------------------------------*\
  173.      Allocate all needed system resources and install a cleanup routine
  174.                            to close them at exit.
  175. \*------------------------------------------------------------------------*/
  176.  
  177. BOOL StartMeUp()
  178.     {
  179.     BOOL fDOSV36;
  180.  
  181.     IntuitionBase   = (struct IntuitionBase*)OpenLibrary( "intuition.library", 0 );
  182.     GfxBase         = (struct GfxBase*)OpenLibrary( "graphics.library", 0 );
  183.     GadToolsBase    = OpenLibrary( "gadtools.library", 0 );
  184.     AslBase         = OpenLibrary( "asl.library", 0 );
  185.  
  186.     /* Extract version information. */
  187.     fDOSV36 = (DOSBase && ((struct Library*)DOSBase)->lib_Version >= 36);
  188.     fIntV39 = (IntuitionBase && ((struct Library*)IntuitionBase)->lib_Version >= 39);
  189.     fGfxV39 = (GfxBase && ((struct Library*)GfxBase)->lib_Version >= 39);
  190.     fGTV39 = (GadToolsBase && GadToolsBase->lib_Version >= 39);
  191.  
  192.     /* Some allocations may fail, but others must not, */
  193.     /* and while we can rely on the standard startup code to have opened */
  194.     /* dos.library for us, we must insist that it's V36+. */
  195.     return( fDOSV36 && IntuitionBase && GfxBase && GadToolsBase );
  196.     }
  197.  
  198.  
  199.  
  200. /*------------------------------------------------------------------------*\
  201.                              Set up the window.
  202. \*------------------------------------------------------------------------*/
  203.  
  204. BOOL BuildGui()
  205.     {
  206.     WORD wIdx;
  207.     LONG lScrTag;
  208.     struct ColorSpec csa[ 33 ];
  209.  
  210.     if( fGfxV39 )
  211.         {                       /* use pen sharing with OS 3.x */
  212.         scr = LockPubScreen( NULL );
  213.         if( !scr )
  214.             goto fail;
  215.         lScrTag = WA_PubScreen;
  216.         for( wIdx = 0; wIdx < 16; wIdx++ )
  217.             laPens[ wIdx ] = ObtainBestPen( scr->ViewPort.ColorMap,
  218.                 wIdx * 0x10000000,
  219.                 wIdx * 0x0A000000,
  220.                 wIdx * 0x05000000,
  221.                 TAG_DONE );     /* shades of brown */
  222.         laPens[ 16 ] = ObtainBestPen( scr->ViewPort.ColorMap,
  223.                 0x00000000,
  224.                 0x40000000,
  225.                 0x80000000,
  226.                 TAG_DONE );     /* blue */
  227.         for( wIdx = 1; wIdx < 16; wIdx++ )
  228.             laPens[ wIdx + 16 ] = ObtainBestPen( scr->ViewPort.ColorMap,
  229.                 wIdx * 0x10000000,
  230.                 wIdx * 0x10000000,
  231.                 wIdx * 0x10000000,
  232.                 TAG_DONE );     /* shades of gray */
  233.         }
  234.     else
  235.         {                       /* else open an own screen */
  236.         for( wIdx = 0; wIdx < 16; wIdx++ )
  237.             {
  238.             csa[ wIdx ].ColorIndex = wIdx;
  239.             csa[ wIdx ].Red   = wIdx;
  240.             csa[ wIdx ].Green = (10 * wIdx)/16;
  241.             csa[ wIdx ].Blue  = (5 * wIdx)/16;
  242.             }                   /* shades of brown */
  243.         csa[ 16 ].ColorIndex = 16;
  244.         csa[ 16 ].Red   = 0;
  245.         csa[ 16 ].Green = 4;
  246.         csa[ 16 ].Blue  = 8;    /* blue */
  247.         for( wIdx = 17; wIdx < 32; wIdx++ )
  248.             {
  249.             csa[ wIdx ].ColorIndex = wIdx;
  250.             csa[ wIdx ].Red   = wIdx - 16;
  251.             csa[ wIdx ].Green = wIdx - 16;
  252.             csa[ wIdx ].Blue  = wIdx - 16;
  253.             }                   /* shades of gray */
  254.         csa[ 32 ].ColorIndex = -1;
  255.         /* Rearrange some colors, so that we start with gray, black, */
  256.         /* white and blue (which are currently at 24, 0, 31, 16) */
  257.         /* and set up brown, dark gray and light gray (8, 18, 28) for */
  258.         /* the cursor sprite (palette colors 17 through 19). */
  259.         csa[ 24 ].ColorIndex = 0;
  260.         csa[  0 ].ColorIndex = 1;
  261.         csa[ 31 ].ColorIndex = 2;
  262.         csa[ 16 ].ColorIndex = 3;
  263.         csa[  8 ].ColorIndex = 17;
  264.         csa[ 28 ].ColorIndex = 19;
  265.         /* Put the palette back in balance. */
  266.         csa[  1 ].ColorIndex =  8;
  267.         csa[  2 ].ColorIndex = 16;
  268.         csa[  3 ].ColorIndex = 24;
  269.         csa[ 17 ].ColorIndex = 28;
  270.         csa[ 19 ].ColorIndex = 31;
  271.         for( wIdx = 0; wIdx < 32; wIdx++ )
  272.             laPens[ wIdx ] = csa[ wIdx ].ColorIndex;
  273.         wIdx = -1;
  274.         scr = OpenScreenTags( NULL,
  275.             SA_Depth,       5,
  276.             SA_DisplayID,   LORES_KEY,
  277.             SA_Title,       (LONG)"Fractal Mountains",
  278.             SA_Colors,      (LONG)&csa,
  279.             SA_Pens,        (LONG)&wIdx,
  280.             TAG_DONE );
  281.         if( !scr )
  282.             goto fail;
  283.         lScrTag = WA_CustomScreen;
  284.         }
  285.     wMaxX = scr->Width;
  286.     wMaxY = scr->Height;
  287.  
  288.     if( !(visualInfo = GetVisualInfoA( scr, NULL )) )
  289.         goto fail;
  290.  
  291.     pwinMain = OpenWindowTags( NULL,
  292.         WA_Width,       320,
  293.         WA_Height,      256,
  294.         WA_AutoAdjust,  TRUE,
  295.         WA_IDCMP,       IDCMP_CLOSEWINDOW | IDCMP_MENUPICK | IDCMP_NEWSIZE
  296.                       | IDCMP_RAWKEY | IDCMP_VANILLAKEY
  297.                       | IDCMP_GADGETUP | IDCMP_GADGETDOWN | IDCMP_INTUITICKS,
  298.         WA_Flags,       WFLG_DEPTHGADGET | WFLG_DRAGBAR | WFLG_CLOSEGADGET
  299.                       | WFLG_SIZEGADGET  | WFLG_SIZEBBOTTOM | WFLG_SIZEBRIGHT
  300.                       | WFLG_NEWLOOKMENUS,
  301.         WA_Activate,    TRUE,
  302.         WA_ScreenTitle, (LONG)"Fractal Mountains",
  303.         lScrTag,        (LONG)scr,
  304.         TAG_DONE );
  305.     if( !pwinMain )
  306.         goto fail;
  307.     WindowLimits( pwinMain, 160, 160, -1L, -1L );
  308.     SetFont( pwinMain->RPort, defFont );
  309.  
  310.     /* Build the menus: */
  311.     menu = CreateMenus( nmaMenu, TAG_DONE );
  312.     if( !menu )
  313.         goto fail;
  314.     LayoutMenus( menu, visualInfo,
  315.         GTMN_NewLookMenus, TRUE,
  316.         TAG_DONE );
  317.     SetMenuStrip( pwinMain, menu );
  318.  
  319.     /* Attach the Gadgets: */
  320.     gadCmd.LeftEdge = -pwinMain->BorderRight + 3;
  321.     gadCmd.TopEdge = pwinMain->BorderTop + 1;
  322.     gadCmd.Width = pwinMain->BorderRight - 4;
  323.     gadCmd.Height = pwinMain->BorderBottom - 2;
  324.     gadRise.LeftEdge = -pwinMain->BorderRight + 3;
  325.     gadRise.TopEdge = pwinMain->BorderTop + pwinMain->BorderBottom;
  326.     gadRise.Width = pwinMain->BorderRight - 4;
  327.     gadRise.Height = -(pwinMain->BorderTop + 2*pwinMain->BorderBottom);
  328.     gadSpin.LeftEdge = 3;
  329.     gadSpin.TopEdge = -pwinMain->BorderBottom + 3;
  330.     gadSpin.Width = -(pwinMain->BorderRight + 3);
  331.     gadSpin.Height = pwinMain->BorderBottom - 4;
  332.     AddGadget( pwinMain, &gadRise, -1 );
  333.     AddGadget( pwinMain, &gadSpin, -1 );
  334.     AddGadget( pwinMain, &gadCmd, -1 );
  335.     RefreshGadgets( &gadRise, pwinMain, NULL );
  336.  
  337.     /* Attach an AreaInfo and a TmpRas to our RastPort, so we can do */
  338.     /* area fill operations on it. */
  339.     ras = AllocRaster( wMaxX, wMaxY );
  340.     if( !ras )
  341.         goto fail;
  342.     InitTmpRas( &tras, ras, RASSIZE( wMaxX, wMaxY ) );
  343.     InitArea( &ari, waArBuf, AREA_BYTES/5 );
  344.     pwinMain->RPort->TmpRas = &tras;
  345.     pwinMain->RPort->AreaInfo = &ari;
  346.  
  347.     return TRUE;
  348.  
  349. fail:
  350.     DestroyGui();
  351.     return FALSE;
  352.     }
  353.  
  354.  
  355.  
  356. /*------------------------------------------------------------------------*\
  357.       Tear down the window and close (or release) the screen it's on.
  358.      Closes the child window first, which shares some of our resources
  359.                             (screen and menus).
  360. \*------------------------------------------------------------------------*/
  361.  
  362. VOID DestroyGui()
  363.     {
  364.     WORD wColor;
  365.  
  366.     if( pwinMain )
  367.         {
  368.         ClearMenuStrip( pwinMain );
  369.         CloseWindow( pwinMain );
  370.         }
  371.     pwinMain = NULL;
  372.     if( ras )
  373.         FreeRaster( ras, wMaxX, wMaxY );
  374.     if( menu )
  375.         FreeMenus( menu );
  376.     menu = NULL;
  377.     if( visualInfo )
  378.         FreeVisualInfo( visualInfo );
  379.     visualInfo = NULL;
  380.     if( scr )
  381.         if( fGfxV39 )
  382.             {
  383.             UnlockPubScreen( NULL, scr );
  384.             for( wColor = 0; wColor < 32; wColor++ )
  385.                 ReleasePen( scr->ViewPort.ColorMap, laPens[ wColor ] );
  386.             }
  387.         else
  388.             CloseScreen( scr );
  389.     scr = NULL;
  390.     }
  391.  
  392.  
  393.  
  394. /*------------------------------------------------------------------------*\
  395.    Disable input to the main window by putting up an invisible requester,
  396.      as shown in the RKRM example. Supply FALSE to enable input again.
  397.    If we have OS3.x, we will also set a busy pointer for the main window
  398.                             while it is locked.
  399. \*------------------------------------------------------------------------*/
  400.  
  401. VOID DisableMainWindow( BOOL fReally )
  402.     {
  403.     static struct Requester rq;
  404.     static BOOL fLocked = FALSE;
  405.  
  406.     if( fReally && !fLocked )
  407.         {
  408.         InitRequester( &rq );
  409.         fLocked = Request( &rq, pwinMain );
  410.         if( fLocked & fIntV39 )
  411.             SetWindowPointer( pwinMain,
  412.                 WA_BusyPointer, TRUE,
  413.                 TAG_DONE );
  414.         }
  415.  
  416.     if( !fReally && fLocked )
  417.         {
  418.         fLocked = FALSE;
  419.         EndRequest( &rq, pwinMain );
  420.         if( fIntV39 )
  421.             SetWindowPointer( pwinMain, TAG_DONE );
  422.         }
  423.     }
  424.  
  425.  
  426.  
  427. /*------------------------------------------------------------------------*\
  428.             Set up or clear a busy pointer for the main window.
  429.                            Only works with OS3.x.
  430.   Use this instead of DisableMainWindow(), if you are still going to check
  431.           the window's message port during whatever you're doing.
  432. \*------------------------------------------------------------------------*/
  433.  
  434. VOID SnoozeMainWindow( BOOL fReally )
  435.     {
  436.     if( fIntV39 )
  437.         SetWindowPointer( pwinMain,
  438.             !fReally ? TAG_DONE :
  439.             WA_BusyPointer,     TRUE,
  440.           /*WA_PointerDelay,    TRUE,*/
  441.             TAG_DONE );
  442.     }
  443.  
  444.  
  445.  
  446. /*------------------------------------------------------------------------*\
  447.           Open a window with some string gadgets and labels in it,
  448.                   as described by the supplied MSR_ITEMs.
  449.    The window's initial position is specified by the IBox parameter, and
  450.   its final position is returned there, too. Note that the *height* of the
  451.                  window is always calculated automatically.
  452.                    The supplied screen must not be NULL.
  453.    In case the "requester" is cancelled, the supplied buffers will remain
  454.                   unchanged and the return value is FALSE.
  455.    Note: This subroutine uses no global variables, so it might be useful
  456.                           in other projects, too.
  457. \*------------------------------------------------------------------------*/
  458.  
  459. VOID GadgetOnOff( struct Window *pwin, struct Gadget *pgad )
  460.     /* Put in a separate function to work around a silly "spilled */
  461.     /* register" error from GCC. */
  462.     {
  463.     GT_SetGadgetAttrs( pgad, pwin, NULL,
  464.         GA_Disabled, TRUE,
  465.         TAG_DONE );
  466.     GT_SetGadgetAttrs( pgad, pwin, NULL,
  467.         GA_Disabled, FALSE,
  468.         TAG_DONE );
  469.     }
  470.  
  471. BOOL MultiStringRequest( struct Screen *pscr, BOOL fPubScreen, struct IBox *pib,
  472.         STRPTR strHail, STRPTR strOk, STRPTR strCancel,
  473.         WORD wItems, MSR_ITEM *pmi )
  474.     {
  475.     struct Window *pwin = NULL;
  476.     struct NewGadget   ng;
  477.     struct Gadget *pgad, *pgadRoot = NULL;
  478.     struct Gadget *pgadFirst = NULL, *pgadLast;
  479.     struct IntuiMessage *intuiMsg;
  480.     struct VisualInfo *pvi;
  481.     char caBuf[ 20 ];
  482.     WORD wFontY = pscr->Font->ta_YSize;
  483.     WORD wStrGadH = wFontY + 8, wButtonH = wFontY + 4;
  484.     WORD wStrSepH = wFontY / 2, wButSepH = wFontY;
  485.     WORD wTopH = wFontY / 2, wBottomH = wFontY / 2;
  486.     WORD wBorderW = wFontY / 2, wLabSepW = wFontY / 2;
  487.     WORD wButtonW, wLabelW, wTemp, wIdx;
  488.     STRPTR str;
  489.     BOOL fQuit = FALSE, fResult = FALSE;
  490.     enum { IDC_STATIC, IDC_OK, IDC_CANCEL, IDC_INPUT };
  491.  
  492.     assert( pscr != NULL );
  493.     if( !(pvi = GetVisualInfoA( pscr, NULL )) )
  494.         return FALSE;
  495.     pwin = OpenWindowTags( NULL,
  496.         WA_Left,        pib->Left,
  497.         WA_Top,         pib->Top,
  498.         WA_Width,       pib->Width,
  499.         WA_InnerHeight, wTopH + wItems*(wStrGadH + wStrSepH) - wStrSepH
  500.                       + wButSepH + wButtonH + wBottomH,
  501.         WA_AutoAdjust,  TRUE,
  502.         WA_IDCMP,       IDCMP_CLOSEWINDOW | IDCMP_RAWKEY | IDCMP_VANILLAKEY
  503.                       | STRINGIDCMP,
  504.         WA_Flags,       WFLG_DEPTHGADGET | WFLG_DRAGBAR | WFLG_CLOSEGADGET,
  505.         WA_Activate,    TRUE,
  506.         WA_Title,       (LONG)strHail,
  507.         !fPubScreen ? WA_CustomScreen :
  508.         WA_PubScreen,   (LONG)pscr,
  509.         TAG_DONE );
  510.     if( pwin == NULL )
  511.         goto fail;
  512.  
  513.     /* Determine the width of the buttons and the labels. */
  514.     str = strCancel;
  515.     wButtonW = TextLength( pwin->RPort, str, strlen( str ) );
  516.     str = strOk;
  517.     wTemp = TextLength( pwin->RPort, str, strlen( str ) );
  518.     if( wButtonW < wTemp )
  519.         wButtonW = wTemp;
  520.     wButtonW = 3 * wButtonW / 2;
  521.     wLabelW = 0;
  522.     for( wIdx = 0; wIdx < wItems; wIdx++ )
  523.         {
  524.         str = pmi[ wIdx ].strLabel;
  525.         if( str != NULL )
  526.             {
  527.             wTemp = TextLength( pwin->RPort, str, strlen( str ) );
  528.             if( wLabelW < wTemp )
  529.                 wLabelW = wTemp;
  530.             }
  531.         }
  532.     if( wLabelW != 0 )
  533.         wLabelW += wLabSepW;
  534.  
  535.     /* Add some gadtools gadgets. */
  536.     pgad = CreateContext( &pgadRoot );
  537.  
  538.     /* string gadgets */
  539.     ng.ng_VisualInfo = pvi;
  540.     ng.ng_TextAttr   = pscr->Font;
  541.     ng.ng_Flags      = 0;
  542.     ng.ng_LeftEdge   = pwin->BorderLeft + wBorderW + wLabelW;
  543.     ng.ng_Width      = pwin->Width - pwin->BorderRight - wBorderW - ng.ng_LeftEdge;
  544.     ng.ng_TopEdge    = pwin->BorderTop + wTopH;
  545.     ng.ng_Height     = wStrGadH;
  546.     ng.ng_GadgetText = NULL;
  547.     for( wIdx = 0; wIdx < wItems; wIdx++ )
  548.         {
  549.         ng.ng_GadgetID = IDC_INPUT + wIdx;
  550.         switch( pmi[ wIdx ].wType )
  551.             {
  552.             case MSRT_STRING:
  553.                 pgad = CreateGadget( STRING_KIND, pgad, &ng,
  554.                     GTST_String,    (LONG)pmi[ wIdx ].val.strBuf,
  555.                     GTST_MaxChars,  pmi[ wIdx ].wBufSize,
  556.                     TAG_DONE );
  557.                 break;
  558.             case MSRT_LONG:
  559.                 pgad = CreateGadget( INTEGER_KIND, pgad, &ng,
  560.                     GTIN_Number,    pmi[ wIdx ].val.lVal,
  561.                     TAG_DONE );
  562.                 break;
  563.             case MSRT_FLOAT:
  564.                 sprintf( caBuf, "%g", pmi[ wIdx ].val.flVal );
  565.                 pgad = CreateGadget( STRING_KIND, pgad, &ng,
  566.                     GTST_String,    (LONG)caBuf,
  567.                     GTST_MaxChars,  sizeof( caBuf ),
  568.                     TAG_DONE );
  569.                 break;
  570.             default:            /* invalid entry type */
  571.                 assert( FALSE );
  572.             }
  573.         pmi[ wIdx ].pgad = pgad;
  574.         ng.ng_TopEdge += wStrGadH + wStrSepH;
  575.         if( pgadFirst == NULL )
  576.             pgadFirst = pgad;
  577.         }
  578.     pgadLast = pgad;
  579.  
  580.     /* OK and Cancel buttons */
  581.     ng.ng_Width      = wButtonW;
  582.     ng.ng_TopEdge   += wButSepH - wStrSepH;
  583.     ng.ng_Height     = wButtonH;
  584.     ng.ng_GadgetText = strOk;
  585.     ng.ng_GadgetID   = IDC_OK;
  586.     pgad = CreateGadget( BUTTON_KIND, pgad, &ng, TAG_DONE );
  587.  
  588.     ng.ng_LeftEdge   = pwin->Width - pwin->BorderRight - wBorderW - wButtonW;
  589.     ng.ng_GadgetText = strCancel;
  590.     ng.ng_GadgetID   = IDC_CANCEL;
  591.     pgad = CreateGadget( BUTTON_KIND, pgad, &ng, TAG_DONE );
  592.  
  593.     if( pgad == NULL )
  594.         goto fail;
  595.  
  596.     AddGList( pwin, pgadRoot, -1, -1, NULL );
  597.     RefreshGList( pgadRoot, pwin, NULL, -1 );
  598.     GT_RefreshWindow( pwin, NULL );
  599.  
  600.     ActivateGadget( pgadFirst, pwin, NULL );
  601.     while( !fQuit )
  602.         {
  603.         intuiMsg = GT_GetIMsg( pwin->UserPort );
  604.         if( !intuiMsg )
  605.             {
  606.             WaitPort( pwin->UserPort );
  607.             continue;
  608.             }
  609.         switch( intuiMsg->Class )
  610.             {
  611.             case IDCMP_GADGETUP:
  612.                 pgad = intuiMsg->IAddress;
  613.                 if( pgad == pgadLast )  /* msg from the last string gadget */
  614.                     {
  615.                     /* Hitting enter in the last string gadget implies */
  616.                     /* closing the requester. Shift-Enter however should */
  617.                     /* enable the user to simply deactivate any string */
  618.                     /* gadgets (so he can type shortcuts to other gadgets, */
  619.                     /* maybe ESC in particular). */
  620.                     if( (intuiMsg->Qualifier
  621.                         & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) != 0 )
  622.                         break;
  623.                     else if( intuiMsg->Code == 0 )
  624.                         {
  625.                         fResult = TRUE;
  626.                         fQuit = TRUE;
  627.                         }
  628.                     else if( intuiMsg->Code == 9 )
  629.                         {
  630.                         /* We don't want TAB to cycle from the last to */
  631.                         /* the first of our string gadgets. */
  632.                         /* To prevent this, we simply deactivate the */
  633.                         /* first string gadget, and the only legal way to */
  634.                         /* do this for a gadtools gadget is by disabling */
  635.                         /* and reenabling it. */
  636.                         GadgetOnOff( pwin, pgadFirst );
  637.                         }
  638.                     else
  639.                         /* On systems with MCP or such installed, we */
  640.                         /* will get here in reply to ESC. */
  641.                         fQuit = TRUE;
  642.                     }
  643.                 else switch( pgad->GadgetID )
  644.                     {
  645.                     case IDC_OK:
  646.                         fResult = TRUE;
  647.                         fQuit = TRUE;
  648.                         break;
  649.                     case IDC_CANCEL:
  650.                         fQuit = TRUE;
  651.                         break;
  652.                     }
  653.                 break;
  654.             case IDCMP_VANILLAKEY:
  655.                 switch( intuiMsg->Code )
  656.                     {
  657.                     case 9:     /* TAB reenables the string gadget gang. */
  658.                         ActivateGadget( pgadFirst, pwin, NULL );
  659.                         break;
  660.                     case 13:
  661.                         fResult = TRUE;
  662.                         fQuit = TRUE;
  663.                         break;
  664.                     case 27:
  665.                         fQuit = TRUE;
  666.                         break;
  667.                     }
  668.                 break;
  669.             case IDCMP_CLOSEWINDOW:
  670.                 fQuit = TRUE;
  671.                 break;
  672.             }
  673.         GT_ReplyIMsg( intuiMsg );
  674.         }
  675.     if(    fResult )
  676.         /* Copy the contents of the string gadgets. */
  677.         for( wIdx = 0; wIdx < wItems; wIdx++ )
  678.             {
  679.             str = ((struct StringInfo *)pmi[ wIdx ].pgad->SpecialInfo)->Buffer;
  680.             switch( pmi[ wIdx ].wType )
  681.                 {
  682.                 case MSRT_STRING:
  683.                     strcpy( pmi[ wIdx ].val.strBuf, str );
  684.                     break;
  685.                 case MSRT_LONG:
  686.                     pmi[ wIdx ].val.lVal = atol( str );
  687.                     break;
  688.                 case MSRT_FLOAT:
  689.                     pmi[ wIdx ].val.flVal = atof( str );
  690.                     break;
  691.                 }
  692.             }
  693.  
  694.     pib->Left   = pwin->LeftEdge;   /* Remember window position */
  695.     pib->Top    = pwin->TopEdge;
  696.     pib->Width  = pwin->Width;
  697.     pib->Height = pwin->Height;
  698. fail:
  699.     if( pwin )
  700.         CloseWindow( pwin );
  701.     if( pgadRoot )
  702.         FreeGadgets( pgadRoot );
  703.     if( pvi )
  704.         FreeVisualInfo( pvi );
  705.  
  706.     return fResult;
  707.     }
  708.  
  709.  
  710.  
  711. /*------------------------------------------------------------------------*\
  712.         Open a window with a string gadget in it and read a string.
  713.    In case the "requester" is cancelled, the supplied buffer will remain
  714.                                  unchanged.
  715. \*------------------------------------------------------------------------*/
  716.  
  717. BOOL StringRequest( STRPTR strHail, STRPTR strBuf, WORD wSize )
  718.     {
  719.     struct IBox ib;
  720.     MSR_ITEM mi;
  721.     BOOL fResult;
  722.  
  723.     mi.strLabel = NULL;
  724.     mi.wType = MSRT_STRING;
  725.     mi.val.strBuf = strBuf;
  726.     mi.wBufSize = wSize;
  727.  
  728.     /* open relative to main window */
  729.     ib.Left  = pwinMain->LeftEdge + 50;
  730.     ib.Top   = pwinMain->TopEdge + 50;
  731.     ib.Width = pwinMain->Width - 100;
  732.  
  733.     DisableMainWindow( TRUE );
  734.     fResult = MultiStringRequest( scr, fGfxV39, &ib, strHail,
  735.             "OK", "Cancel", 1, &mi );
  736.     DisableMainWindow( FALSE );
  737.     return fResult;
  738.     }
  739.  
  740.